home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / infosrvr / dev / libhtml_.tar / MIFwriter.c < prev    next >
C/C++ Source or Header  |  1993-01-21  |  15KB  |  730 lines

  1. /* MIFwriter.c -- MIF output support for WWW
  2.  * $Id: SGMLmain.c,v 1.3 93/01/06 18:40:27 connolly Exp Locker: connolly $
  3.  */
  4.  
  5. /* implements ... */
  6. #include "MIFwriter.h"
  7.  
  8. /* uses ... */
  9. #include "SGML.h"
  10. #include "HTParse.h"
  11. #include "HTMLdtd.h"
  12.  
  13. #include <stdio.h>
  14. #include <string.h>
  15.  
  16. #include "object.h"
  17. #include "debug.h"
  18.  
  19.  
  20. typedef struct{
  21.   char gi[SGML_NAMELEN + 1];
  22.   int content;
  23. }Element;
  24.  
  25.  
  26. typedef struct{
  27.   FILE* out;
  28.   Element stack[SGML_TAGLVL];
  29.   int literal;
  30.   int taglvl;
  31.   int needspace;
  32.   int empty; /* current paragraph is empty */
  33.  
  34.   enum {
  35.     MIFFile,
  36.     VariableFormats, VariableDef,
  37.     TextFlow, ParaLine, Font
  38.     }state;
  39. }MIF;
  40.  
  41. #define STATE(m, s, l, p) ((m)->state=(s),(m)->literal=l, \
  42.                (p) ? ((m)->empty=1,(m)->needspace=0) : 0)
  43.  
  44.  
  45. static HMStartTagProc start_tag;
  46.  
  47. static HMEndTagProc end_tag;
  48.  
  49. static HMDataProc data;
  50.  
  51. static HMFileWriterProc MIFwriter_new;
  52.  
  53. static HMDeleteProc MIFwriter_dt;
  54.  
  55. static VOID
  56.   marker PARAMS((MIF* m,
  57.          CONST HMBinding *attributes,
  58.          int nattrs));
  59.  
  60.  
  61. HMDoc_Class MIFwriter = {MIFwriter_new, 0, MIFwriter_dt,
  62.                start_tag, end_tag, data, html_entity_text};
  63.  
  64.  
  65. static int FrameEncoding[] =
  66. {
  67. /* 160 /space -> */ 0x20,
  68. /* 161 /exclamdown -> */ 0xc1,
  69. /* 162 /cent -> */ 0xa2,
  70. /* 163 /sterling -> */ 0xa3,
  71. /* 164 /currency -> */ 0xdb,
  72. /* 165 /yen -> */ 0xb4,
  73. /* 166 /brokenbar -> */ 0x00,
  74. /* 167 /section -> */ 0xa4,
  75. /* 168 /dieresis -> */ 0xac,
  76. /* 169 /copyright -> */ 0xa9,
  77. /* 170 /ordfeminine -> */ 0xbb,
  78. /* 171 /guillemotleft -> */ 0xc7,
  79. /* 172 /logicalnot -> */ 0xc2,
  80. /* 173 /hyphen -> */ 0x2d,
  81. /* 174 /registered -> */ 0xa8,
  82. /* 175 /macron -> */ 0xf8,
  83. /* 176 /degree -> */ 0x00,
  84. /* 177 /plusminus -> */ 0x00,
  85. /* 178 /twosuperior -> */ 0x00,
  86. /* 179 /threesuperior -> */ 0x00,
  87. /* 180 /acute -> */ 0xab,
  88. /* 181 /mu -> */ 0x00,
  89. /* 182 /paragraph -> */ 0xa6,
  90. /* 183 /periodcentered -> */ 0xe1,
  91. /* 184 /cedilla -> */ 0xfc,
  92. /* 185 /onesuperior -> */ 0x00,
  93. /* 186 /ordmasculine -> */ 0xbc,
  94. /* 187 /guillemotright -> */ 0xc8,
  95. /* 188 /onequarter -> */ 0x00,
  96. /* 189 /onehalf -> */ 0x00,
  97. /* 190 /threequarters -> */ 0x00,
  98. /* 191 /questiondown -> */ 0xc0,
  99. /* 192 /Agrave -> */ 0xcb,
  100. /* 193 /Aacute -> */ 0xe7,
  101. /* 194 /Acircumflex -> */ 0xe5,
  102. /* 195 /Atilde -> */ 0xcc,
  103. /* 196 /Adieresis -> */ 0x80,
  104. /* 197 /Aring -> */ 0x81,
  105. /* 198 /AE -> */ 0xae,
  106. /* 199 /Ccedilla -> */ 0x82,
  107. /* 200 /Egrave -> */ 0xe9,
  108. /* 201 /Eacute -> */ 0x83,
  109. /* 202 /Ecircumflex -> */ 0xe6,
  110. /* 203 /Edieresis -> */ 0xe8,
  111. /* 204 /Igrave -> */ 0xed,
  112. /* 205 /Iacute -> */ 0xea,
  113. /* 206 /Icircumflex -> */ 0xeb,
  114. /* 207 /Idieresis -> */ 0xec,
  115. /* 208 /Eth -> */ 0x00,
  116. /* 209 /Ntilde -> */ 0x84,
  117. /* 210 /Ograve -> */ 0xf1,
  118. /* 211 /Oacute -> */ 0xee,
  119. /* 212 /Ocircumflex -> */ 0xef,
  120. /* 213 /Otilde -> */ 0xcd,
  121. /* 214 /Odieresis -> */ 0x85,
  122. /* 215 /multiply -> */ 0x00,
  123. /* 216 /Oslash -> */ 0xaf,
  124. /* 217 /Ugrave -> */ 0xf4,
  125. /* 218 /Uacute -> */ 0xf2,
  126. /* 219 /Ucircumflex -> */ 0xf3,
  127. /* 220 /Udieresis -> */ 0x86,
  128. /* 221 /Yacute -> */ 0x00,
  129. /* 222 /Thorn -> */ 0x00,
  130. /* 223 /germandbls -> */ 0xa7,
  131. /* 224 /agrave -> */ 0x88,
  132. /* 225 /aacute -> */ 0x87,
  133. /* 226 /acircumflex -> */ 0x89,
  134. /* 227 /atilde -> */ 0x8b,
  135. /* 228 /adieresis -> */ 0x8a,
  136. /* 229 /aring -> */ 0x8c,
  137. /* 230 /ae -> */ 0xbe,
  138. /* 231 /ccedilla -> */ 0x8d,
  139. /* 232 /egrave -> */ 0x8f,
  140. /* 233 /eacute -> */ 0x8e,
  141. /* 234 /ecircumflex -> */ 0x90,
  142. /* 235 /edieresis -> */ 0x91,
  143. /* 236 /igrave -> */ 0x93,
  144. /* 237 /iacute -> */ 0x92,
  145. /* 238 /icircumflex -> */ 0x94,
  146. /* 239 /idieresis -> */ 0x95,
  147. /* 240 /eth -> */ 0x00,
  148. /* 241 /ntilde -> */ 0x96,
  149. /* 242 /ograve -> */ 0x98,
  150. /* 243 /oacute -> */ 0x97,
  151. /* 244 /ocircumflex -> */ 0x99,
  152. /* 245 /otilde -> */ 0x9b,
  153. /* 246 /odieresis -> */ 0x9a,
  154. /* 247 /divide -> */ 0x00,
  155. /* 248 /oslash -> */ 0xbf,
  156. /* 249 /ugrave -> */ 0x9d,
  157. /* 250 /uacute -> */ 0x9c,
  158. /* 251 /ucircumflex -> */ 0x9e,
  159. /* 252 /udieresis -> */ 0x9f,
  160. /* 253 /yacute -> */ 0x00,
  161. /* 254 /thorn -> */ 0x00,
  162. /* 255 /ydieresis -> */ 0xd8,
  163. };
  164.  
  165.  
  166. /* mifwriter constructor */
  167. static HMDoc*
  168. MIFwriter_new(fp)
  169.      FILE* fp;
  170. {
  171.   MIF* m = NEW(MIF, 1);
  172.   m->out = fp;
  173.   m->taglvl = 1;
  174.   strcpy(m->stack[0].gi, "HTML"); /* @@ fake tag minimization */
  175.   STATE(m, MIFFile, 0, 1);
  176.  
  177.   fprintf(m->out,
  178.       "<MIFFile 3.00> # Generated by html2mif\n"
  179.       );
  180.   return (HMDoc*)m;
  181. }
  182.  
  183.  
  184. static VOID
  185. MIFwriter_dt(this)
  186.      HMDoc* this;
  187. {
  188.   FREE(this);
  189. }
  190.  
  191.  
  192.  
  193. static VOID
  194. data(document, chars, nchars)
  195.      HMDoc* document;
  196.      CONST char* chars;
  197.      int nchars;
  198. {
  199.   MIF* m = (MIF*)document;
  200.   Element* e = &m->stack[m->taglvl - 1];
  201.   CONST char* p;
  202.  
  203.   debug(("<emptypar: %d 1st char: %d nchars: %d>\n",
  204.      m->empty, chars[0], nchars));
  205.  
  206.   if(chars[0] == '\n' && nchars <2 &&
  207.      m->literal == 0 && m->empty)
  208.     return;
  209.  
  210.   switch(m->state){
  211.   case MIFFile:
  212.     start_tag((HMDoc*)m, "BODY", 0, 0);
  213.     fprintf(m->out,
  214.         " <Para\n"
  215.         "  <PgfTag `BODY'>\n"
  216.         "  <ParaLine\n"
  217.         "   <String `");
  218.     STATE(m, ParaLine, 0, 1);
  219.     break;
  220.  
  221.   case TextFlow:
  222.     fprintf(m->out,
  223.         " <Para\n"
  224.         "  <PgfTag `%s'>\n"
  225.         "  <ParaLine\n"
  226.         "   <String `", e->gi);
  227.     STATE(m, ParaLine, 0, 1);
  228.     break;
  229.  
  230.   case VariableFormats:
  231.     /* in element content. Skip data */
  232.     return;
  233.  
  234.   case VariableDef:
  235.     /* nothing */
  236.     break;
  237.  
  238.   default:
  239.     fprintf(m->out,
  240.         "   <String `");
  241.   }
  242.  
  243.   for(p = chars; p-chars < nchars; p++){
  244.     if(*p != '\n')
  245.       m->empty = 0;
  246.  
  247.     if(*p & 0x80){
  248.       int i = (*p & 0xFF) - 160;
  249.       if(i < 96) /* in ISOlat1 encoding? */
  250.     printf("\\x%02x ", FrameEncoding[i]);
  251.     }else
  252.       switch(*p){
  253.       case '\n':
  254.     if(m->literal)
  255.       fprintf(m->out,
  256.           "'>\n"
  257.           "   <Char HardReturn>\n"
  258.           "  > # End ParaLine\n"
  259.           "  <ParaLine\n"
  260.           "   <String `");
  261.     else if (m->needspace){
  262.       fprintf(m->out, " ");
  263.       m->needspace = 0;
  264.     }
  265.     break;
  266.     
  267.       case '\r':
  268.     /* nothing */
  269.     break;
  270.     
  271.       case '\t':
  272.     fprintf(m->out, "\\t");
  273.     m->needspace = 0;
  274.     break;
  275.     
  276.       case '>':
  277.     fprintf(m->out, "\\>");
  278.     m->needspace = 1;
  279.     break;
  280.     
  281.       case '\'':
  282.     fprintf(m->out, "\\q");
  283.     m->needspace = 1;
  284.     break;
  285.     
  286.       case '`':
  287.     fprintf(m->out, "\\Q");
  288.     m->needspace = 1;
  289.     break;
  290.     
  291.       case '\\':
  292.     fprintf(m->out, "\\\\");
  293.     m->needspace = 1;
  294.     break;
  295.     
  296.       case ' ':
  297.     if(m->literal){
  298.       fprintf(m->out,
  299.           "'>\n"
  300.           "   <Char HardSpace>\n"
  301.           "   <String `");
  302.     }else{
  303.       m->needspace = 0;
  304.       fprintf(m->out, " ");
  305.     }
  306.     break;
  307.     
  308.       default:
  309.     m->needspace = 1;
  310.     fprintf(m->out, "%c", *p);
  311.       }
  312.   }
  313.   
  314.   fprintf(m->out, "'>\n");
  315. }
  316.  
  317.  
  318. #if 0
  319. /* save this for insets */
  320. static VOID
  321. entity(document, name)
  322.      HMDoc* document;
  323.      CONST char* name;
  324. {
  325.   MIF* m = (MIF*)document;
  326.  
  327.   /*@@ same prep work as data */
  328.   fprintf(m->out, "   <Char %s>\n", name);
  329.   m->needspace = 1;
  330. }
  331. #endif
  332.  
  333.  
  334. static VOID
  335. marker(m, attributes, nattrs)
  336.      MIF* m;
  337.      CONST HMBinding *attributes;
  338.      int nattrs;
  339. {
  340.   int i;
  341.   char* name = 0;
  342.   char* href = 0;
  343.  
  344.   for(i = 0; i < nattrs; i++){
  345.     if(!strcmp(attributes[i].name, "NAME"))
  346.       name = attributes[i].value;
  347.     else if(!strcmp(attributes[i].name, "HREF"))    
  348.       href = attributes[i].value;
  349.   }
  350.  
  351.   if(href){
  352.     char* anchor = HTParse(href, "", PARSE_ANCHOR);
  353.     char* scheme = HTParse(href, "", PARSE_ACCESS);
  354.     char* path = HTParse(href, "", PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
  355.     
  356.     fprintf(m->out,
  357.         "   <Marker\n"
  358.         "    <MType 8>\n");
  359.     if(scheme && *scheme)
  360.       fprintf(m->out,
  361.           "    <MText `message www %s:%s#%s'>\n",
  362.           scheme, path, anchor);
  363.     else if(path && path[0] && path[1]){ /*@@ in case of just "/" */
  364.       if(anchor && *anchor)
  365.     fprintf(m->out,
  366.         "    <MText `gotolink %s:%s'>\n",
  367.         path, anchor);
  368.       else
  369.     fprintf(m->out,
  370.         "    <MText `gotolink %s:firstpage'>\n",
  371.         path);
  372.     }else
  373.       fprintf(m->out,
  374.           "    <MText `gotolink %s'>\n",
  375.           anchor);
  376.     
  377.     fprintf(m->out,
  378.         "   > #End of Marker\n");
  379.     
  380.     free(scheme);
  381.     free(path);
  382.     free(anchor);
  383.   }
  384.   else if (name){
  385.     fprintf(m->out,
  386.         "   <Marker\n"
  387.         "    <MType 8>\n"
  388.         "    <MText `newlink %s'>\n"
  389.         "   > #End of Marker\n",
  390.         name);
  391.   }
  392. }
  393.  
  394.  
  395. static int
  396. start_tag(document, gi, attributes, nattrs)
  397.      HMDoc* document;
  398.      CONST char* gi;
  399.      CONST HMBinding attributes[];
  400.      int nattrs;
  401. {
  402.   MIF* m = (MIF*)document;
  403.   Element* e = &m->stack[m->taglvl++];
  404.   int taglevel = -1;
  405.  
  406.   m->needspace = 0;
  407.  
  408.   strcpy(e->gi, gi);
  409.   debug(("stacking '%s'\n", gi));
  410.  
  411.   if(!strcmp(gi, "H1") ||
  412.      !strcmp(gi, "H2") ||
  413.      !strcmp(gi, "H3") ||
  414.      !strcmp(gi, "H4") ||
  415.      !strcmp(gi, "H5") ||
  416.      !strcmp(gi, "H6") ||
  417.      !strcmp(gi, "PRE") ||
  418.      !strcmp(gi, "XMP") ||
  419.      !strcmp(gi, "LISTING") ||
  420.      !strcmp(gi, "ADDRESS") ||
  421.      !strcmp(gi, "BLOCKQUOTE") ||
  422.      !strcmp(gi, "UL") ||
  423.      !strcmp(gi, "OL") ||
  424.      !strcmp(gi, "MENU") ||
  425.      !strcmp(gi, "DIR") ||
  426.      !strcmp(gi, "DL")
  427.      )
  428.     taglevel = ParaLine;
  429.   else
  430.     if(!strcmp(gi, "A") ||
  431.        !strcmp(gi, "EM") ||
  432.        !strcmp(gi, "TT") ||
  433.        !strcmp(gi, "STRONG") ||
  434.        !strcmp(gi, "B") ||
  435.        !strcmp(gi, "I") ||
  436.        !strcmp(gi, "U") ||
  437.        !strcmp(gi, "CODE") ||
  438.        !strcmp(gi, "SAMP") ||
  439.        !strcmp(gi, "KBD") ||
  440.        !strcmp(gi, "KEY") ||
  441.        !strcmp(gi, "VAR") ||
  442.        !strcmp(gi, "DFN") ||
  443.        !strcmp(gi, "CITE"))
  444.       taglevel = Font;
  445.  
  446.   while(1){
  447.     switch(m->state){
  448.     case MIFFile:
  449.       if(!strcmp(gi, "BODY")){
  450.     fprintf(m->out,    "<TextFlow\n");
  451.     STATE(m, TextFlow, 0, 1);
  452.     return e->content = SGML_MIXED;
  453.       }
  454.  
  455.       else if(!strcmp(gi, "HEAD")){
  456.     return e->content = SGML_ELEMENT;
  457.       }
  458.  
  459.       else if(!strcmp(gi, "TITLE")){
  460.     fprintf(m->out,
  461.         "<VariableFormats\n"
  462.         " <VariableFormat\n"
  463.         "  <VariableName `Title'>\n"
  464.         "  <VariableDef `"
  465.         );
  466.  
  467.     STATE(m, VariableDef, 0, 1);
  468.     return e->content = SGML_RCDATA; /*@@ CDATA? */
  469.       }
  470.  
  471.       else if(!strcmp(gi, "ISINDEX")){
  472.     fprintf(m->out,
  473.         "<VariableFormats\n"
  474.         " <VariableFormat\n"
  475.         "  <VariableName `Index'>\n"
  476.         "  <VariableDef `True'>\n"
  477.         " >\n"
  478.         );
  479.  
  480.     STATE(m, VariableFormats, 0, 1);
  481.     m->taglvl--;
  482.     return SGML_EMPTY;
  483.       }
  484.  
  485.       else if(taglevel == ParaLine || taglevel == Font){
  486.     start_tag((HMDoc*)m, "BODY", 0, 0);
  487.       }
  488.  
  489.       else{
  490.     debug(("'%s' out of context in state %d", gi, m->state));
  491.     m->taglvl--;
  492.     return SGML_EMPTY;
  493.       }
  494.  
  495.       break;
  496.  
  497.  
  498.     case VariableFormats:
  499.       if(!strcmp(gi, "TITLE")){
  500.     fprintf(m->out,
  501.         " <VariableFormat\n"
  502.         "  <VariableName `Title'>\n"
  503.         "  <VariableDef `"
  504.         );
  505.  
  506.     STATE(m, VariableDef, 0, 1);
  507.     return e->content = SGML_RCDATA; /*@@ CDATA? */
  508.       }
  509.  
  510.       else if(!strcmp(gi, "ISINDEX")){
  511.     fprintf(m->out,
  512.         " <VariableFormat\n"
  513.         "  <VariableName `Index'>\n"
  514.         "  <VariableDef `True'>\n"
  515.         " >\n"
  516.         );
  517.  
  518.     m->taglvl--;
  519.     return SGML_EMPTY;
  520.       }
  521.  
  522.       else{
  523.     fprintf(m->out,
  524.         " > #End of VariableFormats\n");
  525.     STATE(m, MIFFile, 0, 1);
  526.       }
  527.       break;
  528.  
  529.  
  530.     case TextFlow:
  531.       if(!strcmp(gi, "PRE")){
  532.     fprintf(m->out,
  533.         " <Para\n"
  534.         "  <PgfTag `%s'>\n"
  535.         "  <ParaLine\n"
  536.         , gi);
  537.     STATE(m, ParaLine, 1, 1);
  538.     return e->content = SGML_MIXED;
  539.       }
  540.  
  541.       else if(!strcmp(gi, "XMP") ||
  542.           !strcmp(gi, "LISTING")){
  543.     fprintf(m->out,
  544.         " <Para\n"
  545.         " <PgfTag `%s'>\n"
  546.         "  <ParaLine\n"
  547.         , gi);
  548.     STATE(m, ParaLine, 1, 1);
  549.     return e->content = SGML_RCDATA;
  550.       }
  551.  
  552.       else if(taglevel == ParaLine){
  553.     fprintf(m->out,
  554.         " <Para\n"
  555.         "  <PgfTag `%s'>\n"
  556.         "  <ParaLine\n"
  557.         , gi);
  558.  
  559.     STATE(m, ParaLine, 0, 1);
  560.     return e->content = SGML_MIXED;
  561.       }
  562.  
  563.       else if(taglevel == Font){
  564.     debug(("%s: transition from TextFlow to BODY ParaLine", gi));
  565.  
  566.     fprintf(m->out,
  567.         " <Para\n"
  568.         "  <PgfTag `BODY'>\n"
  569.         "  <ParaLine\n");
  570.     
  571.     STATE(m, ParaLine, 0, 1);
  572.       }
  573.  
  574.       else{
  575.     debug(("'%s' out of context in state %d", gi, m->state));
  576.     m->taglvl--;
  577.     return SGML_EMPTY;
  578.       }
  579.  
  580.       break;
  581.  
  582.     case ParaLine:
  583.       if(!strcmp(gi, "A")){
  584.     fprintf(m->out,
  585.         "   <Font\n"
  586.         "    <FTag `%s'>\n"
  587.         "   >\n", gi);
  588.  
  589.     marker(m, attributes, nattrs);
  590.  
  591.     STATE(m, Font, m->literal, 0);
  592.     return e->content = SGML_MIXED;
  593.       }
  594.  
  595.       else if(taglevel == Font){
  596.     fprintf(m->out,
  597.         "   <Font\n"
  598.         "    <FTag `%s'>\n"
  599.         "   >\n"
  600.         , gi);
  601.  
  602.     STATE(m, Font, m->literal, 0);
  603.     return e->content = SGML_MIXED;
  604.       }
  605.  
  606.       else if(!strcmp(gi, "P")){
  607.     m->taglvl--;
  608.     if(!m->empty)
  609.       fprintf(m->out,
  610.           "  > # End ParaLine\n"
  611.           " > # End Para\n");
  612.     STATE(m, TextFlow, 0, 1);
  613.     return SGML_EMPTY;
  614.       }
  615.  
  616.       else if(!strcmp(gi, "DT") ||
  617.           !strcmp(gi, "LI")){
  618.     m->taglvl--;
  619.     if(!m->empty)
  620.       fprintf(m->out,
  621.           "  > # End ParaLine\n"
  622.           " > # End Para\n"
  623.           " <Para\n"
  624.           "  <ParaLine\n");
  625.     
  626.     m->empty = 1;
  627.     m->needspace = 0;
  628.     return SGML_EMPTY;
  629.       }
  630.  
  631.       else if(!strcmp(gi, "DD")){
  632.     fprintf(m->out,
  633.         "   <Char Tab>\n");
  634.  
  635.     m->taglvl--;
  636.     return SGML_EMPTY;
  637.       }
  638.  
  639.       else if(taglevel = ParaLine){
  640.     debug(("'%s' start tag: back to TextFlow state\n", gi));
  641.     fprintf(m->out,
  642.         "  > # End of ParaLine\n"
  643.         " > # End of Para\n"
  644.         );
  645.     STATE(m, TextFlow, 0, 1);
  646.       }
  647.  
  648.       else{
  649.     debug(("'%s' out of context in state %d", gi, m->state));
  650.     m->taglvl--;
  651.     return SGML_EMPTY;
  652.       }
  653.  
  654.       break;
  655.  
  656.     default:
  657.       debug(("state %d unexpected (<%s>)\n", m->state, gi));
  658.       m->taglvl--;
  659.       return SGML_EMPTY;
  660.     }
  661.   }
  662. }
  663.  
  664.  
  665.  
  666. static VOID
  667. end_tag(document, gi)
  668.      HMDoc* document;
  669.      CONST char* gi;
  670. {
  671.   MIF* m = (MIF*)document;
  672.   Element* e;
  673.   int i;
  674.  
  675.   for(i = m->taglvl - 1; i>=0; i--){
  676.     debug(("found </%s>. stack has %s\n", gi, m->stack[i].gi));
  677.     if(m->stack[i].content == SGML_RCDATA ||
  678.        m->stack[i].content == SGML_CDATA ||
  679.        !strcmp(gi, m->stack[i].gi))
  680.       break;
  681.   }
  682.  
  683.   if(i < 0){
  684.     debug(("Parse error: '%s' end tag with no such element open.\n", gi));
  685.     return;
  686.   }
  687.  
  688.   while(m->taglvl > i){
  689.     m->taglvl--;
  690.     switch(m->state){
  691.     case VariableDef:
  692.       fprintf(m->out,
  693.           " > #End of VariableFormat\n");
  694.       STATE(m, VariableFormats, 0, 1);
  695.       break;
  696.  
  697.     case VariableFormats:
  698.       fprintf(m->out,
  699.           "> #End of VariableFormats\n");
  700.       STATE(m, MIFFile, 0, 1);
  701.       break;
  702.  
  703.     case TextFlow:
  704.       fprintf(m->out,
  705.           "> # End of TextFlow\n");
  706.       STATE(m, MIFFile, 0, 1);
  707.       break;
  708.  
  709.     case ParaLine:
  710.       fprintf(m->out,
  711.           "  > # End of ParaLine\n"
  712.           " > # End of Para\n");
  713.       STATE(m, TextFlow, 0, 1);
  714.       break;
  715.  
  716.     case Font:
  717.       fprintf(m->out,
  718.           "   <Font\n"
  719.           "    <FTag `'>\n"
  720.           "   > # End of Font\n");
  721.       STATE(m, ParaLine, m->literal, 0);
  722.       break;
  723.  
  724.     default:
  725.       debug(("'%s' end tag unexpected in state %d.", gi, m->state));
  726.     }
  727.   }
  728. }
  729.  
  730.